package org.longteng.sys;

import lombok.extern.slf4j.Slf4j;
import org.longteng.core.utils.StringExUtils;

import java.io.*;
import java.util.concurrent.*;

import static java.lang.System.out;

/**
 * 执行系统指令工具类
 */
@Slf4j
public class SysExecCmdUtil {
    /** 1:win,2:linux,3:mac,0:other */
    public static final int osType = checkOsType();

    /**
     * 返回操作系统标识号
     * @return
     */
    private static int checkOsType() {
        final String osName = System.getProperty("os.name");
        int osTypeFlag = 0;
        if (osName.matches("^(?i)Windows.*$")) {
            osTypeFlag = 1;
        } else if(osName.matches("^(?i)Linux.*$")) {
            osTypeFlag = 2;
        } else if(osName.toUpperCase().contains("MAC")) {
            osTypeFlag = 3;
        }
        return osTypeFlag;
    }
    private static String cmdReadEncode = System.getProperty("sun.jnu.encoding"); // GBK
    private static ThreadPoolExecutor executor;
    static {
        ThreadFactory namedThreadFactory = Executors.defaultThreadFactory();
        // new ThreadFactoryBuilder().setNameFormat("cmd-pool-%d").build();
        //根据实际情况创建线程池
        executor = new ThreadPoolExecutor(10,
                20,
                5,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(1024),
                namedThreadFactory,
                new ThreadPoolExecutor.AbortPolicy());
    }

    /**
     * 返回调用命令的入口
     * @param command
     * @return
     */
    public static String[] getShellCommand(String command) {
        String shell = osType == 1 ? "cmd" : "/bin/bash";
        String c = osType == 1 ? "/c" : "-c" ;

        String[] cmd = {shell, c, command};
        return cmd;
    }

    //  ===============================
    /**
     * 运行外部命令并获得返回结果,执行内部命令用(dir,date,tree ...)，先调getShellCommand
     * <br>如：["cmd","-c","dir c:/log"]
     *
     * @param command 需要运行的外部命令
     * @return 合并标准输出和错误输出的返回结果
     */
    public static StringBuffer buildProcess(String[] command) {
        StringBuffer builder = new StringBuffer();
        try {
            Process process = Runtime.getRuntime().exec(command);
            try (SequenceInputStream input = new SequenceInputStream(process.getInputStream(),
                    process.getErrorStream());
                ) {
                threadExecReadStream(input, builder);  // 在线程中消费命令进度的内容
                markExecInput(process,"");  // 模拟执行到指令需要输入的场景

                process.waitFor();
                process.destroy();
            } catch (InterruptedException e) {
                log.error("cmd exec error。Interrupted!",e);
                builder.append("CMDException:").append(e.getMessage());
                Thread.currentThread().interrupt();
            }
        } catch (IOException e) {
            log.error("cmd exec IO error。",e);
            builder.append("CMDException:").append(e.getMessage());
        }
        return builder;
    }

    /**
     * 在线程中消费命令进度的内容
     * @param stream
     */
    private static void threadExecReadStream(InputStream stream, StringBuffer outRetBuffer) {
        //处理buffer的线程
        executor.execute(new Runnable() {
            @Override
            public void run() {
                String line = null;
                try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, cmdReadEncode));) {
                    while ((line = in.readLine()) != null) {
                        out.println(line);
                        if(outRetBuffer != null) {
                            outRetBuffer.append(line);
                            outRetBuffer.append(System.lineSeparator());
                        }
                    }
                } catch (IOException e) {
                    log.error("exec error : {}", e);
                }
            }
        });
    }

    /**
     * 模拟执行到指令需要输入的场景
     * @param p
     * @param inData
     */
    private static void markExecInput(Process p, String inData) {
        try(OutputStream os = p.getOutputStream();) {
            PrintStream ps2 = new PrintStream(os);
            ps2.println(inData+"\n\r");
        }catch (Exception e) {
            log.error("模拟执行到指令需要输入错误.", e);
        }
    }

    /**
     * 运行外部命令并获得返回结果
     * <br>直接指令串，如：exeProcess("curl -i www.baidu.com");
     * @param command
     * @param ret4startsStr 取结果的标志
     * @return
     */
    public static String exeProcess(String command, String ret4startsStr) {
        StringBuilder builder = new StringBuilder();
        try {
            Process process = Runtime.getRuntime().exec(command);
            try (SequenceInputStream input = new SequenceInputStream(process.getInputStream(),
                    process.getErrorStream());
                 BufferedReader reader = new BufferedReader(new InputStreamReader(input
                         ,osType==1?"GBK":"UTF-8"))
                ) {
                String msg;
                while ((msg = reader.readLine()) != null) {
                    out.println(msg);
                    if(StringExUtils.isNotEmpty(ret4startsStr)) {
                        if (msg.startsWith(ret4startsStr)) {   // 如： "Date:"
                            builder.append(msg);
                            break;
                        }
                    } else {
                        builder.append(msg);
                        builder.append(System.lineSeparator());
                    }
                }
                process.waitFor();
                process.destroy();
            } catch (InterruptedException e) {
                log.error("cmd exec error。Interrupted!",e);
                builder.append("CMDException:").append(e.getMessage());
                Thread.currentThread().interrupt();
            }
        } catch (IOException e) {
            log.error("cmd exec IO error。",e);
            builder.append("CMDException:").append(e.getMessage());
        }
        return builder.toString();
    }

    /**
     * 运行外部命令并获得返回结果
     * <br>如：["cmd","-c",""]
     * @param command
     * @return
     */
    public static String runProcess(String[] command) {
        StringBuffer builder = buildProcess(command);
        if (builder.length() == 0) {
            return "";
        } else {
            return builder.substring(0, builder.length() - System.lineSeparator().length());
        }
    }


    /*
    public static void main(String[] args) {
        String builder = exeProcess("curl -i www.baidu.com");
        System.out.println(builder);
        for (String str : builder.split(" ")) {
            if (str.indexOf(":") > 0 && str.indexOf(":") < 3) {
                String str2 = utcToLocal(str, Time_Pattern);
                SimpleDateFormat localFormater = new SimpleDateFormat(Day_Pattern);
                Date now = new Date();
                String newDate = localFormater.format(now) + " " + str2;
                System.out.println(newDate);
                StringBuilder cmdBuilder = new StringBuilder();
                cmdBuilder.append("date -s \"");
                cmdBuilder.append(newDate + "\"");
                String runProc = runProcess(getCommand(cmdBuilder.toString()));
                System.out.println(runProc);
                break;
            }
        }
    }
    */

}
